package com.suray.system.run.random.code;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.path.core.Route;
import com.suray.basic.wcs.path.core.api.impl.RouteFactoryImp;
import com.suray.basic.wcs.path.enums.NodeType;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.system.run.random.code.util.NavNode;
import com.suray.system.run.random.code.util.NodePoint;
import com.suray.wcs.res.enums.RgvStatus;
import com.suray.wcs.service.core.util.LockNodeManager;
import com.suray.wcs.service.enums.TaskType;
import com.suray.wcs.service.init.SystemInit;
import com.suray.wcs.service.pojo.Rgv;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.NodeDBServiceImpl;
import com.suray.wcs.service.system.service.impl.TaskDBServiceImpl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class LadderNavigate {
    private NodeDBServiceImpl nodeDBService = SpringContextHolder.getBean(NodeDBServiceImpl.class);
    private TaskDBServiceImpl taskDBService = SpringContextHolder.getBean(TaskDBServiceImpl.class);

    private Rgv rgv;

    /**
     * 用来划分区域的两点
     * 两点确定一块区域
     */
    private Node node1, node2;

    /**
     * 当前层的所有节点
     */
    private List<Node> layerMap;

    public LadderNavigate(Rgv rgv, Node node1, Node node2, boolean xDir) {
        this.rgv = rgv;

        this.layerMap = nodeDBService.getNodeByLayer(Collections.singletonList(rgv.getCurLocation().getZ()));
        this.node1 = node1;
        this.node2 = node2;
        this.xDir = xDir;
    }

    /**
     * 货位方向是否是x方向（上下）
     */
    private boolean xDir;

    public void navigate() {
        if (rgv.getCurLocation() == null) {
            throw new RuntimeException("请先同步小车位置");
        }

        NavNode[][] map = analyzeMap(node1, node2);
        generalNavigateTask(new LadderNavigateCore(map, calcNodePoint(map, rgv.getCurLocation())).analyzePath());
    }

    /**
     * 生成跑码导航任务
     * @param path
     */
    private void generalNavigateTask(List<NavNode> path) {
        if (path.size() < 2) {
            throw new RuntimeException("当前区域的导航节点不足两个，无法生成跑码任务");
        }

        Iterator<NavNode> nodeIterator = path.iterator();
        List<Node> coords = new ArrayList<>();
        NavNode lastNode = nodeIterator.next();
        coords.add(lastNode);

        while (nodeIterator.hasNext()) {
            NavNode node = nodeIterator.next();

            if (!lastNode.near(node)) {
                generalNavigateTask(rgv.getRgvDB().getRgvNo(), coords, taskDBService, layerMap);
                coords.clear();
            }
            lastNode = node;
            coords.add(lastNode);
        }
        generalNavigateTask(rgv.getRgvDB().getRgvNo(), coords, taskDBService, layerMap);
    }

    public static void generalNavigateTask(int rgvNo, List<Node> pathNodes, TaskDBServiceImpl taskDBService,
                                           List<Node> layerMap) {
        TaskDB taskdb = new TaskDB();
        Rgv rgv = SystemInit.getByRgvNo(rgvNo);

        if (pathNodes.size() < 2) {
            throw new RuntimeException("路径节点小于2个，请选择两个以上的节点");
        }

        if (rgv.getRgvStatus() == RgvStatus.OFF_LINE) {
            throw new RuntimeException("小车离线");
        }

        if (rgv.getCurLocation() == null) {
            throw new RuntimeException("小车位置不存在，请同步小车位置");
        }

        int floor = rgv.getCurLocation().getZ();

        StringBuffer path = new StringBuffer();
        Node preNode = null;
        boolean flag = true;
        for (Node node : pathNodes) {
            if (preNode == null) {
                preNode = node;
                if (preNode.getZ() != floor) {
                    throw new RuntimeException("小车不在起点层，不能执行跑码任务, 请把小车开到需要跑码的楼层");
                }
            } else {
                if (preNode.getZ() != node.getZ()) {
                    throw new RuntimeException("路径坐标不在同一层:" + preNode + "-" + node);
                }

                if (preNode.getX() != node.getX() && preNode.getY() != node.getY()) {
                    throw new RuntimeException("路径坐标不在一条直线上:" + preNode + "-" + node);
                }
                preNode = node;
            }

            for (Node node1 : layerMap) {
                if (node.equals(node1)) {
                    checkNodeType(node1);

                    break;
                }
            }

            if (flag) {
                flag = false;
            } else {
                path.append("_");
            }
            path.append(node.toString());
        }

        taskdb.setStartNode(pathNodes.get(0));
        taskdb.setEndNode(pathNodes.get(pathNodes.size() - 1));
        taskdb.setTaskType(TaskType.RUN_FOR_GCODE);
        taskdb.setProcessingRgvNo(rgvNo);

        taskdb.setPriorityCode(0);

        taskdb.setPath(path.toString());

        taskDBService.insertTask(Collections.singletonList(taskdb));
        LoggerUtil.info("生成跑码任务:" + taskdb, LadderNavigate.class);
    }

    public static void checkNodeType(Node node) {
        if (node.getNodeType() == NodeType.AD
                || node.getNodeType() == NodeType.L
                || node.getNodeType() == NodeType.TL
                || node.getNodeType() == NodeType.AE
                || node.getNodeType() == NodeType.TE
                || node.getNodeType() == NodeType.ST
        ) {
            throw new RuntimeException("选中的范围不能包含'" + node.getNodeType().getDescription() + "'，请重新选择");
        }
    }

    /**
     * 计算离小车最近的顶点
     * @param map
     * @param startNode
     * @return
     */
    private NodePoint calcNodePoint(NavNode[][] map, Node startNode) {
        NavNode leftUp = map[0][0], rightUp = map[0][map[0].length - 1], leftDown = map[map.length - 1][0],
                rightDown = map[map.length - 1][map[0].length - 1];

        if (leftUp == null && rightUp == null && leftDown == null && rightDown == null) {
            throw new RuntimeException("选中的范围，4个顶点不能都是禁用点");
        }

        RouteFactoryImp routeFactoryImp = new RouteFactoryImp(layerMap, LockNodeManager.getLockedNodes(),
                rgv.generateWCSTimeModuleBO(), 0);
        Route routeLeftUp = leftUp == null ? null : routeFactoryImp.createRoute(startNode, leftUp, rgv.isHasCargo());
        Route routeLeftDown = leftDown == null ? null :
                routeFactoryImp.createRoute(startNode, leftDown, rgv.isHasCargo());
        Route routeRightUp = rightUp == null ? null : routeFactoryImp.createRoute(startNode, rightUp, rgv.isHasCargo());
        Route routeRightDown = rightDown == null ? null :
                routeFactoryImp.createRoute(startNode, rightDown, rgv.isHasCargo());

        NodePoint node = null;
        Route route = null;
        Integer dir = null;

        if (routeLeftUp != null && (route == null || route.getCost() > routeLeftUp.getCost())) {
            node = new NodePoint(0, 0);
            route = routeLeftUp;
            dir = 1;
        }

        if (routeLeftDown != null && (route == null || route.getCost() > routeLeftDown.getCost())) {
            node = new NodePoint(map.length - 1, 0);
            route = routeLeftDown;
            dir = 2;
        }

        if (routeRightUp != null && (route == null || route.getCost() > routeRightUp.getCost())) {
            node = new NodePoint(0, map[0].length - 1);
            route = routeRightUp;
            dir = 3;
        }

        if (routeRightDown != null && (route == null || route.getCost() > routeRightDown.getCost())) {
            node = new NodePoint(map.length - 1, map[0].length - 1);
            route = routeRightDown;
            dir = 4;
        }

        analyzeLadder(map, dir);

        return node;
    }

    private void analyzeLadder(NavNode[][] map, int dir) {
        if (dir == 1 || dir == 3) {
            for (int i = 0, ladder = 1; i < map.length; i++, ladder++) {
                for (int j = 0; j < map[i].length; j++) {
                    NavNode navNode = map[i][j];
                    if (navNode == null) {
                        continue;
                    }
                    navNode.setLadder(ladder);
                }
            }
        } else if (dir == 2 || dir == 4) {
            for (int i = map.length - 1, ladder = 1; i >= 0; i--, ladder++) {
                for (int j = 0; j < map[i].length; j++) {
                    NavNode navNode = map[i][j];
                    if (navNode == null) {
                        continue;
                    }
                    navNode.setLadder(ladder);
                }
            }
        }
    }

    /**
     * 解析跑码范围
     * @param node1
     * @param node2
     * @return
     */
    private NavNode[][] analyzeMap(Node node1, Node node2) {
        int dx = node2.getX() - node1.getX(), dy = node2.getY() - node1.getY();
        dx = dx == 0 ? 1 : (dx / Math.abs(dx));
        dy = dy == 0 ? 1 : (dy / Math.abs(dy));

        if (xDir) {
            return calcNavNodes(node1.getY(), node1.getX(), node2.getY(), node2.getX(), node1.getZ(), dy, dx);
        } else {
            return calcNavNodes(node1.getX(), node1.getY(), node2.getX(), node2.getY(), node1.getZ(), dx, dy);
        }
    }

    /**
     * 计算需要导航的地图
     * @param startX
     * @param startY
     * @param endX
     * @param endY
     * @param z
     * @param dx
     * @param dy
     * @return
     */
    private NavNode[][] calcNavNodes(int startX, int startY, int endX, int endY, int z, int dx, int dy) {
        NavNode[][] nodes = new NavNode[Math.abs(endX - startX) + 1][Math.abs(endY - startY) + 1];

        int offsetX = 0, offsetY = 0;
        if (dx > 0) {
            offsetX = startX;
        } else {
            offsetX = endX;
        }

        if (dy > 0) {
            offsetY = startY;
        } else {
            offsetY = endY;
        }

        for (int i = startX, ladder = 1; i != (endX + dx); i += dx, ladder++) {
            for (int j = startY; j != (endY + dy); j += dy) {
                NavNode node = new NavNode(findNode(new Node(i, j, z)));

                checkNodeType(node);

                if (node.getNodeType() == NodeType.B) {
                    continue;
                }

                nodes[i - offsetX][j - offsetY] = node;
            }
        }

        return nodes;
    }

    private Node findNode(Node node) {
        return layerMap.get(layerMap.indexOf(node));
    }

    /**
     * 查找跑码范围的4个定点，离小车近的那个点
     * @param node1
     * @param node2
     * @return
     */
    private Node findRgvNearNode(Node node1, Node node2) {
        return null;
    }

    /**
     * 小车前往起点
     * @param node
     */
    private void runToNode(Node node) {

    }

    /**
     * 解析跑码路径
     * @return
     */
    private List<Node> analyzeRunPath() {
        return null;
    }

    /**
     * 开始乱序跑码
     */
    private void startRandomCode() {

    }
}
